package org.duomn.ichat.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.plaf.SplitPaneUI;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import org.duomn.ichat.dao.UserDaoForTest;
import org.duomn.ichat.entity.FontAttribute;
import org.duomn.ichat.entity.Message;
import org.duomn.ichat.entity.MsgCatalog;
import org.duomn.ichat.entity.MsgType;
import org.duomn.ichat.entity.StyleChatMessage;
import org.duomn.ichat.entity.User;
import org.duomn.ichat.exception.MsgLoseException;
import org.duomn.ichat.gui.compose.FontPane;
import org.duomn.ichat.gui.compose.FontPane.FontChangeListener;
import org.duomn.ichat.gui.custom.HtmlLetterWrapKit;
import org.duomn.ichat.net.UIChatMsgHandler;
import org.duomn.ichat.thread.TCPReceiverThread;
import org.duomn.ichat.thread.TCPThreadAbstract;
import org.duomn.ichat.thread.TCPThreadFactory;
import org.duomn.ichat.util.Const;
import org.duomn.ichat.util.DateUtil;
import org.duomn.ichat.util.GBC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 聊天窗口的主界面
 * @author duomn
 *
 */
public class ChatFrame extends JFrame implements UIChatMsgHandler {

	private static final long serialVersionUID = -6522907226322329627L;
	
	private static Logger logger = LoggerFactory.getLogger(ChatFrame.class);
	
	public static final int CHAT_WIDTH = 540;
	public static final int CHAT_HEIGHT = 520;
	
	private static final int SEND_ENTER = 0;
	private static final int SEND_CTRL_ENTER = 1;
	private static final Color txtBgColor = Color.WHITE;
	private static final Font lblFont = new Font("Microsoft Yahei", Font.BOLD, 12);
	
	private static Map<Integer, ChatFrame> map = new HashMap<Integer, ChatFrame>();
	
	private static Map<String, MutableAttributeSet> fontSets = new HashMap<String, MutableAttributeSet>();
	
	int sendType = SEND_CTRL_ENTER; // Ctrl+Enter
	JPanel basePane;
	JSplitPane leftSplitPane;
	JTextPane recordPane;
	JTextPane chatPane;
	JScrollPane scrollPane;
	
	JLabel splitBtn; 
	
	User user;
	TCPThreadAbstract chatThread;
	
	private FontAttribute fontSet;
	private MutableAttributeSet meAttr;
	private MutableAttributeSet otherAttr;
	
	public synchronized static ChatFrame getInstance(User user) {
		ChatFrame cf = map.get(user.getId());
		if (cf == null) {
			cf = new ChatFrame(user);
			map.put(user.getId(), cf);
		}
		return cf;
	}
	
	private ChatFrame(User user) {
		this.user = user;
		String name = user.getName() == null ? String.valueOf(user.getId()) : user.getName();
		setTitle("正在与" + name + "聊天");
		setSize(CHAT_WIDTH, CHAT_HEIGHT);
		setLocationByPlatform(true);
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				close();
			}
		});
		
		JPanel topPane = new JPanel();
		topPane.add(new JLabel("top lable"));
		topPane.setPreferredSize(new Dimension(100, 60));
		getContentPane().add(topPane, BorderLayout.NORTH);
		
		basePane = new JPanel(new GridBagLayout());
		basePane.setMinimumSize(new Dimension(540, 400));
		basePane.add(initLeftPane(), new GBC(0, 0, 1, 1).setFill(GBC.BOTH).setWeight(1, 1));
		basePane.add(initRigthPane(), new GBC(1, 0, 1, 1).setFill(GBC.VERTICAL).setWeight(0, 1));
		
		getContentPane().add(basePane, BorderLayout.CENTER);
		setVisible(true);
		leftSplitPane.setDividerLocation(0.6); // 比例
		
		chatPane.requestFocus();
		
		meAttr = new SimpleAttributeSet();
		StyleConstants.setFontFamily(meAttr, "微软雅黑");
		StyleConstants.setFontSize(meAttr, 12);
		StyleConstants.setForeground(meAttr, new Color(0, 128, 64));
		
		otherAttr = new SimpleAttributeSet();
		StyleConstants.setFontFamily(otherAttr, "微软雅黑");
		StyleConstants.setFontSize(otherAttr, 12);
		StyleConstants.setForeground(otherAttr, new Color(0, 57, 253));
	}
	
	private void close() {
		if (chatThread != null && !chatThread.isRunning()) {
			chatThread.closeSession();
		}
		map.remove(ChatFrame.this.user.getId());
		for (CloseListener closeListener : closeList) {
			closeListener.close();
		}
	}
	
	public void setChatThread(TCPThreadAbstract sender) {
		this.chatThread = sender;
	}
	
	private JPanel initLeftPane() {
		leftSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
		leftSplitPane.setResizeWeight(0.6);
		leftSplitPane.setDividerSize(1);

		// record窗口
		recordPane = new JTextPane();
		recordPane.setEditorKit(new HtmlLetterWrapKit()); // 兼容Java7字母不换行的bug
		recordPane.setContentType("text/html"); 
		recordPane.setEditable(false);
		recordPane.setBackground(txtBgColor);
		scrollPane = new JScrollPane(recordPane, 
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		JPanel leftTop = new JPanel(new BorderLayout());
		JPanel funcPanel = new JPanel();
		funcPanel.setPreferredSize(new Dimension(100, 30));
		funcPanel.setBackground(Color.GRAY);
		leftTop.add(funcPanel, BorderLayout.NORTH);
		leftTop.add(scrollPane, BorderLayout.CENTER);
		leftTop.setMinimumSize(new Dimension(100, 140));
		leftSplitPane.setTopComponent(leftTop);
		
		// chat窗口
		JPanel inputPane = new JPanel(new BorderLayout());
		JPanel toolPane = initFontPane();
		inputPane.add(toolPane, BorderLayout.NORTH);
		chatPane = new JTextPane();
		chatPane.setEditorKit(new HtmlLetterWrapKit()); // 兼容Java7字母不换行的bug
		chatPane.setContentType("text/html"); 
		chatPane.setBackground(txtBgColor);
		chatPane.setParagraphAttributes(getInputFontAttr(fontSet), false); // 让第一次输入生效
		chatPane.setMinimumSize(new Dimension(100, 50));
		chatPane.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) { // 键盘按下事件
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					int enterType = 0;
					e.consume();
					if (e.getModifiers() == InputEvent.CTRL_MASK) { // Ctrl+Enter
						enterType = SEND_CTRL_ENTER; 
					} else { // Enter
						enterType = SEND_ENTER;
					}
					
					if (enterType == sendType) { // 是否和当前设定的发送方式相同
						sendMsg();
					} else {
						Document doc = chatPane.getDocument();
						try {
							doc.insertString(doc.getLength(), "\n", null);
						} catch (BadLocationException e1) {
							e1.printStackTrace();
						}
					}
				}
			}
		});
		new DropTarget(chatPane, DnDConstants.ACTION_COPY_OR_MOVE, new DropTargetAdapter() {
			@SuppressWarnings("unchecked")
			public void drop(DropTargetDropEvent dtde) {
				if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
					dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
					List<File> list = null;
					try {
						list =  (List<File>) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
					} catch (UnsupportedFlavorException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
					String temp = "";
					if (list != null) {
						for (File file : list) {
							temp += file.getAbsoluteFile() + ";\n";
						}
					}
					JOptionPane.showMessageDialog(null, temp);
					dtde.dropComplete(true); // 指示拖拽完成
				} else {
					dtde.rejectDrop();  // 拒绝拖拽
				}
			}
		});
		
		inputPane.add(chatPane, BorderLayout.CENTER);
		JPanel sendTool = new JPanel(new FlowLayout(FlowLayout.TRAILING));
		JButton closeBtn = new JButton("关闭");
		closeBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ChatFrame.this.dispose();
				close();
			}
		});
		JButton sendBtn = new JButton("发送");
		sendBtn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				sendMsg();
			}
		});
		sendTool.add(closeBtn);
		sendTool.add(sendBtn);
		inputPane.add(sendTool, BorderLayout.SOUTH);
		leftSplitPane.setBottomComponent(inputPane);
		
		SplitPaneUI ui = leftSplitPane.getUI();  
		if (ui instanceof BasicSplitPaneUI) {  
		    ((BasicSplitPaneUI) ui).getDivider().setBorder(null);  
		}  
		JPanel leftPane = new JPanel(new BorderLayout());
		leftPane.add(leftSplitPane, BorderLayout.CENTER);
		return leftPane;
	}
	
	private JPanel initFontPane() {
		FontPane fp = new FontPane();
		this.fontSet = fp.getFontAttr();
		fp.addFontChangeListener(new FontChangeListener() {
			public void fontChange(FontAttribute fontAttr) {
				if (chatPane != null) {
					fontSet = fontAttr;
					AttributeSet attr = getInputFontAttr(fontAttr);
					StyledDocument doc = chatPane.getStyledDocument();
					doc.setParagraphAttributes(0, doc.getLength(), attr, false);
				}
			}
		});
		return fp;
	}
	
	/** 创建聊天输入窗口的样式 */
	public MutableAttributeSet getInputFontAttr(FontAttribute fontAttr) {
		String key = (fontAttr == null) ? "LOCAL_DEFAULT_FONT" : "LOCAL_DEFAULT_FONT" + fontAttr.toString();
		return getFontAttr(key, fontAttr, false);
	}
	
	/** 创建聊天记录窗口的样式 */
	public MutableAttributeSet getRecordFontAttr(FontAttribute fontAttr) {
		String key = (fontAttr == null) ? "REMOTE_DEFAULT_FONT" : "REMOTE_DEFAULT_FONT" + fontAttr.toString();
		return getFontAttr(key, fontAttr, true);
	}
	
	private MutableAttributeSet getFontAttr(String key, FontAttribute fontAttr, boolean remote) {
		MutableAttributeSet attrs = fontSets.get(key);
		
		if (attrs == null) { // 缓存创建的样式 
			if (fontAttr == null) { // 无样式时选用，应该用不到
				attrs = new SimpleAttributeSet();
			} else { // 根据字符创建
				attrs = new SimpleAttributeSet();
				StyleConstants.setFontFamily(attrs, fontAttr.getFontName()); // 设置字体
				StyleConstants.setFontSize(attrs, fontAttr.getFontSize()); // 字体大小
				StyleConstants.setBold(attrs, fontAttr.isBold()); // 粗体字
				StyleConstants.setItalic(attrs, fontAttr.isItalic());  // 斜体字
				StyleConstants.setUnderline(attrs, fontAttr.isUnderline()); // 下滑线
				StyleConstants.setForeground(attrs, Color.gray);  // 字体颜色
			}
			if (remote) {
				StyleConstants.setLeftIndent(attrs, 16);
				StyleConstants.setRightIndent(attrs, 16);
			}
			fontSets.put(key, attrs);
		}
		
		return attrs;
	}
	
	private JPanel initRigthPane() {
		JLabel adLbl = new JLabel();
		adLbl.setHorizontalAlignment(SwingConstants.CENTER);
		adLbl.setFont(lblFont);
		adLbl.setPreferredSize(new Dimension(140, 30));
		adLbl.setText("iChat1.0");
		
		JPanel othersPane = new JPanel(); 
		othersPane.setLayout(new GridBagLayout());
		JLabel onameLbl = new JLabel("其他姓名：");
		othersPane.add(onameLbl, new GBC(0, 0, 1, 1));
		//test
		othersPane.setBorder(BorderFactory.createLineBorder(Color.GRAY));
		othersPane.setMinimumSize(new Dimension(140, 100));
		
		JPanel mePane = new JPanel();
		JLabel mnameLbl = new JLabel("自己姓名：");
		mePane.add(mnameLbl);
		// test
		mePane.setBorder(BorderFactory.createLineBorder(Color.GRAY));
		
		final JPanel rightPane = new JPanel(new GridBagLayout());
		rightPane.add(adLbl, new GBC(1, 0, 1, 1).setFill(GBC.BOTH).setWeight(1, 0.f).setMargin(1));
		rightPane.add(othersPane, new GBC(1, 1, 1, 1).setFill(GBC.BOTH).setWeight(1, 0.6f).setMargin(1));
		rightPane.add(mePane, new GBC(1, 2, 1, 1).setFill(GBC.BOTH).setWeight(1, 0.4f).setMargin(1));
		rightPane.setPreferredSize(new Dimension(140, CHAT_HEIGHT));
		// test
		rightPane.setBorder(BorderFactory.createLineBorder(Color.GRAY));
		
		splitBtn = new JLabel();
		splitBtn.setSize(new Dimension(5, 100));
		splitBtn.setBackground(Color.green);
		splitBtn.setVisible(false);
		rightPane.add(splitBtn, new GBC(0, 0, 1, 2).setAnchor(GBC.CENTER));
		
		rightPane.addMouseMotionListener(new MouseMotionAdapter() {
			boolean selected = false;
			public void mouseMoved(MouseEvent e) {
				int x = e.getX();
				int y = e.getY();
				if (x < 5 && y > 100 && y < CHAT_HEIGHT - 100) {
					if (!selected) {
						splitBtn.setVisible(true);
						rightPane.repaint();
//						System.out.println("选中了该区域：");
						selected = true;
					}
				} else {
					if (selected) {
						splitBtn.setVisible(false);
						rightPane.repaint();
						selected = false;
					}
				}
			}
		});
		
		return rightPane;
	}
	
	private void sendMsg() {
		// 获取无样式的文本
		Document doc = chatPane.getDocument(); 
		String plainText = "";
		try {
			plainText = doc.getText(0, doc.getLength());
			doc.remove(0, doc.getLength());
		} catch (BadLocationException e2) {
			logger.error("", e2);
		}
		final String input = plainText; // end 获取无样式的文本
		
		// 把聊天信息写入到记录框
		String name = Const.<User>get(Const.USER_SELF).getName();
		StringBuffer buf = new StringBuffer();
		buf.append(name).append(" ").append(DateUtil.getNowStr());
		
		try {
			writeRecord(buf.toString(), meAttr);
			writeRecord(input, getRecordFontAttr(fontSet));
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		} // end 把聊天信息写入到记录框
		
		new SwingWorker<Boolean, Void>() {

			protected Boolean doInBackground() throws Exception {
				boolean sendFlag = true; // 发送是否成功
				if (chatThread == null || chatThread.isRunning()) { // 创建一个主动的Socket
					if (user.getPort() == 0) { // 
						logger.debug("用户可能不在线");
						sendFlag = false;
					} 
					
					if (sendFlag) { 
						chatThread = TCPThreadFactory.createChatMsgPortThread(
								user.getHost(), user.getPort(), ChatFrame.this);
						if (!chatThread.openSession()) { // 打开失败直接返回
							sendFlag = false;
						}
					}
				}
				
				if (sendFlag) {  
					StyleChatMessage scMsg = new StyleChatMessage();
					scMsg.src = Const.get(Const.USER_SELF_ID);
					scMsg.msgCatalog = MsgCatalog.CHAT_MSG;
					scMsg.msgType = MsgType.CHAT_INFO;
					scMsg.msg = input;
					scMsg.fontAttr = fontSet.clone();
					try {
						chatThread.send(scMsg);
					} catch (MsgLoseException e) {
						sendFlag = false;
					}
				}
				return sendFlag;
			}

			@Override
			protected void done() {
				try {
					if (!get()) {
						try {
							writeRecord("\t通讯异常，消息发送失败！", null);
						} catch (BadLocationException e) {
							logger.error("写入消息失败提示，发生异常", e);
						}
					}
				} catch (InterruptedException e) {
					logger.error("发生消息的SwingWorker发生Interrupt异常", e);
				} catch (ExecutionException e) {
					logger.error("发生消息的SwingWorker发生执行异常", e);
				}
			}
		}.execute();;
	}
	
	public void handle(final Message msg) {
		if (msg instanceof StyleChatMessage) {
			logger.debug("receive msg");
			final StyleChatMessage scMsg = (StyleChatMessage) msg;
			int src = scMsg.src;
			User user = Const.<Map<Integer, User>>get(Const.USER_MAP).get(src); 
			String name = user == null ? String.valueOf(src) : user.getName();
			StringBuffer buf = new StringBuffer();
			buf.append(name).append(" ").append(DateUtil.getNowStr());
			final String chatInfo = buf.toString(); 
			
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					try {
						writeRecord(chatInfo, otherAttr);
						logger.debug(scMsg.msg + "," + scMsg.fontAttr);
						writeRecord(scMsg.msg, getRecordFontAttr(scMsg.fontAttr));
					} catch (BadLocationException e) {
						logger.error("写入文档发生错误", e);
					}
				}
			});
		} else {
			logger.warn("收到不能处理的消息");
		}
	}
	
	private synchronized void writeRecord(String content, AttributeSet attr) throws BadLocationException {
		StyledDocument doc = recordPane.getStyledDocument();
		// 在最后一行添加换行字符，修正最后一行是单行时，样式不正确的bug
		if (doc.getLength() == 0) {
			doc.insertString(doc.getLength(), content + "\n\n", attr);
		} else {
			doc.remove(doc.getLength() - 2, 2); // 先移除最后一个换行字符
			doc.insertString(doc.getLength(), "\n" + content + "\n\n", attr);
		}
		
		// 让滚动条到最下方
		if (scrollPane != null) {
			scrollPane.getViewport().doLayout();
			JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
			scrollBar.setValue(scrollBar.getMaximum());
		}
	}
	
	/** 添加关闭的监听，让关闭按钮的操作与右上角的关闭图标的操作一致 */
	private List<CloseListener> closeList = new ArrayList<CloseListener>();
	
	public void addCloseListener(CloseListener closeLisener) {
		closeList.add(closeLisener);
	}
	
	public void removeCloseListener(CloseListener closeLisener) {
		closeList.remove(closeLisener);
	}
	
	public interface CloseListener {
		void close();
	}
	
	public static void main(String[] args) {
		System.setProperty("java.awt.im.style","on-the-spot"); // 去掉输入法的窗口
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				try {
//					UIManager.setLookAndFeel("com.nilo.plaf.nimrod.NimRODLookAndFeel");
//					UIManager.setLookAndFeel("com.seaglasslookandfeel.SeaGlassLookAndFeel");
//					SubstanceLookAndFeel.setSkin(new BusinessBlackSteelSkin());
					UIManager.getLookAndFeelDefaults().put("defaultFont", new Font("Microsoft Yahei",Font.PLAIN,12));
				} catch (Exception e) {
					e.printStackTrace();
				}
				
				// 由MainFrame启动时提供
				int port = 8090;
				Const.put(Const.USER_MAP, new UserDaoForTest().listFriends());
				Const.put(Const.USER_SELF_PORT, port);
				Const.put(Const.USER_SELF_ID, 0); // 记录用户当前的id
				Map<Integer, User> userMap = Const.get(Const.USER_MAP); 
				for (Entry<Integer, User> item : userMap.entrySet()) {
					if (Const.<Integer>get(Const.USER_SELF_ID) == item.getKey()) {
						item.getValue().setPort(Const.<Integer>get(Const.USER_SELF_PORT));
						Const.put(Const.USER_SELF, item.getValue());
					}
				}
				final TCPReceiverThread receiver = new TCPReceiverThread(port); 
				receiver.start();
				
				// 由当前Frame提供
				User user = new User();
				user.setId(0);
				user.setHost("127.0.0.1");
				user.setPort(port);
				user.setName("张三");
				user.setStatus("人生没有下雨的晴天");
				ChatFrame cf = ChatFrame.getInstance(user); 
				cf.addCloseListener(new CloseListener() {
					public void close() {
						receiver.close();
					}
				});
			}
		});
	}

}
